package com.example.consumptionrecords.service.impl;

import com.example.consumptionrecords.config.shiro.PhoneAuthCodeToken;
import com.example.consumptionrecords.entity.dto.LoginUserDTO;
import com.example.consumptionrecords.entity.dto.RegisterUserDTO;
import com.example.consumptionrecords.entity.enums.GlobalConstants;
import com.example.consumptionrecords.entity.pojo.User;
import com.example.consumptionrecords.entity.pojo.UserInfo;
import com.example.consumptionrecords.entity.vo.UserVO;
import com.example.consumptionrecords.exception.UserException;
import com.example.consumptionrecords.mapper.UserMapper;
import com.example.consumptionrecords.service.interfaces.UserService;
import com.example.consumptionrecords.util.random.RandomUtil;
import com.example.consumptionrecords.util.redis.RedisUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

/**
 * 孙敬佳
 * 2024 \ 02 \ 06
 */

@Service
@Slf4j
public class UserServiceImpl implements UserService {


	@Resource
	private UserMapper userMapper;

	@Resource
	private RedisUtil redisUtil;

	@Override
	public UserVO userLogin(LoginUserDTO loginUserDTO, HttpServletResponse response) {
		// 创建shiro认证主体
		Subject subject = SecurityUtils.getSubject();
		try {
			// 先进行用户名密码登录
			UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(loginUserDTO.getUserName(), loginUserDTO.getUserPassword());
			subject.login(usernamePasswordToken);
		} catch (UnknownAccountException e) {
			// 当用户名密码登录失败，再进行短信验证码登录。两者都失败，抛出异常
			PhoneAuthCodeToken phoneAuthCodeToken = new PhoneAuthCodeToken(loginUserDTO.getUserPhone(), loginUserDTO.getAuthCode());
			subject.login(phoneAuthCodeToken);
			redisUtil.delete(loginUserDTO.getUserPhone());
		}
		// 将token存入响应头
		response.setHeader("Authorization", subject.getSession().getId().toString());
		// 获取用户信息
		User user = (User) subject.getPrincipal();
		// 获取用户详细信息
		UserInfo userInfo = userMapper.selectUserInfoByUserId(user.getId());
		// 封装返回结果
		UserVO userVO = new UserVO();
		BeanUtils.copyProperties(user, userVO);
		BeanUtils.copyProperties(userInfo, userVO);
		return userVO;
	}

	@Override
	public String getTotalConsumption(Integer userId) {
		return userMapper.getTotalConsumption(userId);
	}


	@Override
	public String register(RegisterUserDTO registerUserDTO) {
		// 判断验证码是否有效
		if (!redisUtil.exists(registerUserDTO.getUserPhone())) {
			throw new UserException(GlobalConstants.USER_AUTH_CODE_TIMEOUT_ERR.getCode(), GlobalConstants.USER_AUTH_CODE_TIMEOUT_ERR.getMessage());
		}
		// 判断验证码是否正确
		if (registerUserDTO.getAuthCode().equals(redisUtil.get(registerUserDTO.getUserPhone()))) {
			User user = new User();

			// 判断手机号是否已存在
			if (userMapper.userPhoneIsExists(registerUserDTO.getUserPhone())) {
				throw new UserException(GlobalConstants.USER_PHONE_EXISTS_ERR.getCode(), GlobalConstants.USER_PHONE_EXISTS_ERR.getMessage());
			}

			// 拷贝注册信息
			BeanUtils.copyProperties(registerUserDTO, user);

			// 密码加密
			String salt = new SecureRandomNumberGenerator().nextBytes().toString();
			user.setUserSalt(salt);
			String encodePassword = new SimpleHash("MD5", user.getUserPassword(), salt, 2).toString();
			user.setUserPassword(encodePassword);

			// 生成用户名
			boolean userNameExists = true;
			String userName = "";
			while (userNameExists) {
				userName = RandomUtil.getRandomNum(10, false);
				userNameExists = userMapper.userNameIsExists(userName);
			}
			user.setUsername(userName);

			// 昵称
			user.setUserNickName("小账" + RandomUtil.getRandomNum(6, false));

			// 插入用户
			userMapper.insertUser(user);
			redisUtil.delete(registerUserDTO.getUserPhone());
			return userName;
		}
		// 验证码错误
		throw new UserException(GlobalConstants.USER_AUTH_CODE_WRONG.getCode(), GlobalConstants.USER_AUTH_CODE_WRONG.getMessage());
	}
}
